package com.zj.netty.client;

import com.zj.netty.utils.Constant;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.io.UnsupportedEncodingException;

public class ClientNetty {
    // 要请求的服务器的ip地址
    private String ip;
    // 服务器的端口
    private int port;

    public ClientNetty(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    // 请求端主题
    private void start() throws InterruptedException, UnsupportedEncodingException {
        /**
         * Netty用于接收客户端请求的线程池职责如下。
         * （1）接收客户端TCP连接，初始化Channel参数；
         * （2）将链路状态变更事件通知给ChannelPipeline
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        try {
            // 它是客户端辅助启动类，功能类似于ServerBootstrap
            Bootstrap bs = new Bootstrap();
            // 将NioEventLoopGroup实例绑定到Bootstrap对象中
            bs.group(bossGroup)
/**
 * 创建Channel（典型的channel有NioSocketChannel，NioServerSocketChannel，OioSocketChannel，
 * OioServerSocketChannel，EpollSocketChannel，EpollServerSocketChannel），区别与服务端，
 * 这里创建的是NIOSocketChannel
 */
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.SO_KEEPALIVE, true)
/**
 * 建立连接后的具体Handler。注意这里区别与服务端，使用的是handler()而不是childHandler()。
 * handler和childHandler的区别在于，handler是接受或发送之前的执行器；childHandler为建立连接之后的执行器。
 */
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 处理来自服务端的响应信息
                            socketChannel.pipeline().addLast(new ClientHandler());
                        }
                    });
            // 发起异步连接操作
            ChannelFuture cf = bs.connect(ip, port).sync();

            String reqStr = "我是客户端请求1$_--->";
            // 发送客户端的请求
            cf.channel().writeAndFlush(Unpooled.copiedBuffer(reqStr.getBytes(Constant.charset)));
            Thread.sleep(300);
            cf.channel().writeAndFlush(Unpooled.copiedBuffer("我是客户端请求2$_--->".getBytes(Constant.charset)));
            Thread.sleep(300);
            cf.channel().writeAndFlush(Unpooled.copiedBuffer("我是客户端请求3$_--->".getBytes(Constant.charset)));

            // 等待直到连接中断
            cf.channel().closeFuture().sync();
        } finally {
            // 优雅退出，释放NIO线程组
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws UnsupportedEncodingException, InterruptedException {
        new ClientNetty("127.0.0.1", Constant.serverSocketPort).start();
    }
}
